#pragma once
#include <string>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include "comm.hpp"

const std::string pathname = ".";
const int projid = 0x66;
const int shmsize = 4096;
const int defaultid = -1;
#define CREATER "creater"
#define USER "user"

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

class Shm
{
private:
    // 创建shm并得到shmid
    void Creat()
    {
        key_t key = ftok(pathname.c_str(), projid);
        _id = shmget(key, _size, IPC_CREAT | IPC_EXCL | 0666);
        if (_id < 0)
            ERR_EXIT("shmget");
        std::cout << "creat success" << std::endl;
    }
    // 得到shmid
    void Get()
    {
        key_t key = ftok(pathname.c_str(), projid);
        _id = shmget(key, shmsize, IPC_CREAT);
        if (_id < 0)
            ERR_EXIT("shmget");
        std::cout << "get success" << std::endl;
    }
    // 挂载shm
    void ShmAt()
    {
        _start_mem = shmat(_id, nullptr, 0);
        if ((long long)_start_mem < 0)
            ERR_EXIT("shmat");
    }
    // 解除连接
    void Detach()
    {
        int ret = shmdt(_start_mem);
        if(ret < 0)
            ERR_EXIT("shmdt");
    }
    // 删除shm
    void Destroy()
    {
        int ret = shmctl(_id, IPC_RMID, nullptr);
        if (ret < 0)
            ERR_EXIT("shmctl");
    }

public:
    Shm(const char *nametype)
        : _nametype(nametype), _size(shmsize), _id(defaultid), _start_mem(nullptr)
    {
        if(_nametype == CREATER)
            Creat();
        else if(_nametype == USER)
            Get();
        else
            ERR_EXIT("Shm");
        ShmAt();
    }

    // 得到虚拟内存
    void *VirtualAdd()
    {
        printf("%s->VirtualAdd:%p\n", _nametype.c_str(), _start_mem);
        return _start_mem;
    }

    ~Shm()
    {
        Detach();
        if (_nametype == CREATER)
            Destroy();
    }

private:
    int _id;
    std::string _nametype;
    void *_start_mem;
    int _size;
};